package org.test;

import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.IdUtil;
import org.opencv.core.Point;
import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgproc.Imgproc;

import java.awt.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;

public class TraceBoundaryProcessor {

    /**
     * 获取轨迹覆盖的面积及边框的GPS坐标
     *
     * @param traces      GPS坐标数组，每个元素包含'lng'和'lat'
     * @param tillWidth   耕种宽度，单位：米
     * @param tolerance   容忍面积参数，当空洞面积小于该参数时忽略空洞，单位：平方米，默认为0
     * @param scale       建议缩放比例，实际使用时会考虑内存限制
     * @param memoryLimit 算法使用的内存限制，单位：MB
     * @param debug       是否返回 img(耕种覆盖图在cv2坐标系下的图像) 和 traces(cv2坐标下的边界点坐标列表，
     *                    列表长度与 boundaries相同)，默认为False
     * @return 一个dict对象，包含
     * area:       耕种总面积，除去未耕种的空洞，单位：平方米
     * boundaries: 边界数组，每个元素包括
     * points:  gps坐标数组
     * type:    'filled'或'blank'
     * subarea: 该边界覆盖的面积，单位平方米
     * img:    耕种覆盖图在cv2坐标系下的图像，仅debug=True时返回
     * traces: cv2坐标下的边界点坐标列表，列表长度与 boundaries相同，仅debug=True时返回
     */
    public static Map<String, Object> getTracesBoundaries(Collection<GPSPoint> traces, double tillWidth, double tolerance, double scale, int memoryLimit, boolean wantFewPoints, boolean debug) {

        scale = Math.min(Math.max(scale, 4.0 / tillWidth), 100 / tillWidth);

        // 坐标转换
        GPSTransformResult traces_in_cv2 = GPSTraceProcessor.gpsToCV2(traces, tillWidth, scale, memoryLimit);
        List<GPSPoint> points = traces_in_cv2.points;

        scale = traces_in_cv2.scale;
        int adj_till_width = traces_in_cv2.adjTillWidth;
        int origin_shift = traces_in_cv2.originShift;
        GPSPoint base_gps = traces_in_cv2.originGPS;

        List<Point> pointList = new ArrayList<>();
        for (int i = 0; i < points.size(); i++) {
            GPSPoint point = points.get(i);
            pointList.add(new Point(point.lng, point.lat));
        }
        MatOfPoint polyline = new MatOfPoint();
        polyline.fromList(pointList);

        // cv2的坐标顺序是(height, width),返回的坐标点是(x, y),对应关系是x -> width, y -> height
        long size_height = Math.round(traces_in_cv2.maxPointY + origin_shift);
        long size_width = Math.round(traces_in_cv2.maxPointX + origin_shift);

        Mat img = Mat.zeros(new Size(size_width, size_height), CvType.CV_8UC1);

        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        // 绘制轨迹
        Imgproc.polylines(img, Arrays.asList(polyline), false, new Scalar(255, 255, 255), (int) adj_till_width);

        // 释放内存
        polyline.release();

        if (debug) {
            String id = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            Image bufferedImage = HighGui.toBufferedImage(img);
            ImgUtil.write(bufferedImage, new File(id + "_" + IdUtil.nanoId().substring(0, 5).toLowerCase() + ".png"));
        }

        // 查找轮廓
        Imgproc.findContours(img, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_TC89_KCOS);

        // 释放内存
        img.release();

        List<Map<String, Object>> boundaries = new ArrayList<>();
        double area = 0;

        for (int i = 0; i < contours.size(); i++) {
            MatOfPoint subMat = contours.get(i);
            double subarea = Imgproc.contourArea(subMat) / (scale * scale);

            if (subarea > tolerance) {
                List<GPSPoint> boundaryPoints = new ArrayList<>();
                for (Point p : subMat.toArray()) {
                    GPSPoint gp = cv2ToGPS(base_gps, p, origin_shift, scale);
                    boundaryPoints.add(gp);
                }
                if (wantFewPoints) {
                    boundaryPoints = refinePoints(boundaryPoints, tillWidth);
                }

                int[] hierarchyData = new int[4];
                hierarchy.get(i, 0, hierarchyData);
                int parent = hierarchyData[3]; // 获取父轮廓索引

                Map<String, Object> boundary = new HashMap<>();
                boundary.put("points", boundaryPoints);

                boundary.put("type", parent == -1 ? "filled" : "blank");
                boundary.put("type", "filled");

                boundary.put("subarea", subarea);
                boundaries.add(boundary);
                if ("filled".equals(boundary.get("type"))) {
                    area += subarea;
                } else {
                    area -= subarea;
                }
            }
            // 释放内存
            subMat.release();
        }

        // 释放内存
        hierarchy.release();

        Map<String, Object> result = new HashMap<>();
        result.put("area", area);
        result.put("boundaries", boundaries);
        return result;
    }

    public static List<GPSPoint> refinePoints(List<GPSPoint> points, double tillWidth) {
        // 实现点简化逻辑
        boolean changed = true;
        while (points.size() > 3 && changed) {
            int i = 1;
            changed = false;

            while (i + 1 < points.size()) {
                // 去掉无效的点
                double dist = GPSUtils.distance(points.get(i), points.get(i + 1));
                if (dist <= tillWidth) {
                    changed = true;
                    points.remove(i);
                } else {
                    i += 1;
                }
            }
        }

        return points;
    }

    public static GPSPoint cv2ToGPS(GPSPoint baseGPS, Point point, double shift, double scale) {
        // 实现cv2坐标到GPS坐标的转换
        return CoordinateConverter.cv2ToGPS(baseGPS, point.x, point.y, shift, scale);
    }
}
